import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;

import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PFont;

public class CategoryBins {
	private PApplet parent = null;
	@SuppressWarnings("unused")
	private Tooltip tooltip = null;
	
	private ArrayList<Bin> bins = null;
		
	// CategoryBins Fonts
	private PFont fontBinName = null;
	private PFont fontIssue = null;

	// Issue Bin Spacing
	private int maxIssues = 1;
	
	private int ISSUESWIDE = 1;
	private int ISSUESTALL = 1;
	
	final private int ISSUEMAX = 100;
	final private int ISSUEMIN = 5;
	
	// BG Rect Coordinates and Colors
	private int BGX1 = 0;
	private int BGY1 = 0;
	private int BGX2 = 0;
	private int BGY2 = 0;
	private int BGSTROKE = 0;
	private int BGFILL = 0;

	private boolean showBackground = false;
	
	// Issue and Bin Spacing
	private int BINSPACING = 0;
		
	public CategoryBins(PApplet p, Tooltip t) {
		this.parent = p;
		this.tooltip = t;
		this.bins = new ArrayList<Bin>();
		this.fontBinName = parent.createFont("FFScala", 12);
		this.fontIssue = parent.createFont("FFScala", 12);

		setup();
	}

	public void setup() {
		// Modify These
		BGSTROKE = parent.color(0,150,0);
		BGFILL = parent.color(0,150,0);		
	}
	
	public void update(int x1, int y1, int x2, int y2, int spacing, boolean show) {
		// Don't Modify These
		BGX1 = x1;
		BGY1 = y1;
		BGX2 = x2;
		BGY2 = y2;
		BINSPACING = spacing;
		showBackground = show;
		
		if(this.bins.size() == 0)
			return;
		
		for(int i = 0; i < this.bins.size(); i++)
			this.bins.get(i).update(BGX1+(((BGX2-BGX1)/bins.size())*i), BGY1, BGX1+(((BGX2-BGX1)/bins.size())*(i+1)), BGY2, BINSPACING, 5);

		// Issue Size Scaling
		for(int i = ISSUEMAX; i >= ISSUEMIN; i--)
			if( (this.bins.get(0).BINX2-this.bins.get(0).BINX1)/i * (this.bins.get(0).BINY2-this.bins.get(0).BINY1)/i >= maxIssues ) {
					ISSUESWIDE = (this.bins.get(0).BINX2-this.bins.get(0).BINX1)/i;
					ISSUESTALL = (this.bins.get(0).BINY2-this.bins.get(0).BINY1)/i;
					return;
				}
		
		ISSUESWIDE = (this.bins.get(0).BINX2-this.bins.get(0).BINX1)/ISSUEMIN;
		ISSUESTALL = (this.bins.get(0).BINY2-this.bins.get(0).BINY1)/ISSUEMIN;
	}

	public void setMaxIssuesPerBin(int max) {
		maxIssues = max;
	}
	
	public void loadBins(ArrayList<Integer> id, ArrayList<String> name, ArrayList<String> desc) {
		this.bins.clear();
		for(int i = 0; i < id.size(); i++) {
			Bin b = new Bin();
			b.setProjectIssueId(id.get(i));
			b.setName(name.get(i));
			b.setDescription(desc.get(i));
			this.bins.add(b);
		}
	}
		
	public void addIssue(Issue iss) {
		for(int i = 0; i < bins.size(); i++)
			if(bins.get(i).getProjectIssueId() == iss.getProjectIssueId()) {
				bins.get(i).addIssue(iss);
				break;
			}
	}
	
//	public void addDeadIssue(Issue iss) { System.out.println("added");
//		for(int i = 0; i < bins.size(); i++)
//			if(bins.get(i).getProjectIssueId() == iss.getProjectIssueId()) {
//				bins.get(i).addDeadIssue(iss);
//				break;
//			}
//	}

	public void removeIssue(Issue iss) {
		for(int i = 0; i < bins.size(); i++)
			bins.get(i).removeIssue(iss);
	}
	
	public boolean isClicked() {
		if(BGX1 <= parent.mouseX && parent.mouseX <= BGX2 && BGY1 <= parent.mouseY && parent.mouseY <= BGY2)
			return true;
		
		return false;
	}
	
	public void display(double curTime, int timeInc) {
		parent.rectMode(PConstants.CORNERS);
		
		// Draw Background
		if(showBackground) {
			parent.stroke(BGSTROKE);
			parent.fill(BGFILL);
			parent.strokeWeight(1);
			parent.rect(BGX1, BGY1, BGX2, BGY2);
		}
				
		// Draw Bins
		for(int i = 0; i < this.bins.size(); i++)
			this.bins.get(i).display(curTime, timeInc);
	}
	
	private class Bin {
		private CopyOnWriteArrayList<Issue> issues = null;
		private CopyOnWriteArrayList<Issue> deadIssues = null;
		
		private int TOP = 0;
		private int BOTTOM = 0;
		
		private int BINX1 = 0;
		private int BINY1 = 0;
		private int BINX2 = 0;
		private int BINY2 = 0;
		private int BINSTROKE = 0;
		private int BINFILL = 0;
		private int BINWEIGHT = 0;

		private int TEXTX = 0;
		private int TEXTY = 0;
		private int TEXTSTROKE = 0;
		private int TEXTFILL = 0;

		private int project_issue_id = 0;
		private String name = "";
		private String description = "";
		
		Bin() {
			this.issues = new CopyOnWriteArrayList<Issue>();
			this.deadIssues = new CopyOnWriteArrayList<Issue>();
			setup();
		}

		public void setup() {
			// Adjust these
			BINSTROKE = parent.color(255,255,255);
			BINFILL = parent.color(255,255,255);
			TEXTSTROKE = parent.color(255,255,255);
			TEXTFILL = parent.color(255,255,255);			
		}
		
		public void update(int x1, int y1, int x2, int y2, int spacing, int weight) {			
			// Don't adjust these
			TOP = y1;
			BOTTOM = y2;
						
			BINX1 = x1+(spacing/2);
			BINY1 = y1+(y2-y1)/5;
			BINX2 = x2-(spacing/2);
			BINY2 = y2-((y2-y1)/5+fontBinName.theight);
			BINWEIGHT = weight;			
			
			TEXTX = x1+((x2-x1)/2);
			TEXTY = BINY2+fontBinName.theight;
			
			updateIssues();
		}
		
		public void updateIssues() {
			if(issues.size() == 0)
				return;
						
			int issuespacingx, issuespacingy;

			issuespacingx = (BINX2-BINX1)/ISSUESWIDE;
			issuespacingy = (BINY2-BINY1)/ISSUESTALL;			

			int k = 0;
			for(int i = 0; i < ISSUESTALL; i++)
				for(int j = 0; j < ISSUESWIDE && k < issues.size(); j++) {
					issues.get(k).update(BINX2-((j+1)*issuespacingx),BINY2-((i+1)*issuespacingy),BINX2-(j*issuespacingx),BINY2-(i*issuespacingy), TOP, BOTTOM);
					k++;
				}
		}
		

		public void addIssue(Issue iss) {
			if(issues.contains(iss))
				return;
			
			issues.add(iss);
			updateIssues();
		}
		
		public void removeIssue(Issue iss) {
			if(!issues.remove(iss))
				return;

			deadIssues.add(iss);
			updateIssues();
		}

//		public void addDeadIssue(Issue iss) {
//			if(deadIssues.contains(iss))
//				return;
//
//			deadIssues.add(iss);
//			updateIssues();			
//		}
		
		public void display(double curTime, int timeInc) {
			parent.stroke(BINSTROKE);
			parent.fill(BINFILL);
			parent.strokeWeight(BINWEIGHT);
			parent.line(BINX1, BINY1, BINX1, BINY2);
			parent.line(BINX1, BINY2, BINX2, BINY2);
			parent.line(BINX2, BINY1, BINX2, BINY2);
			
			int issueCount = 0;
			
			parent.textFont(fontIssue);
			for(Issue i : issues) {
				i.display(curTime, timeInc);
				if(i.getOpenDate() <= curTime && (i.getCloseDate() > curTime || i.getCloseDate() == 0))
					issueCount++;
			}
			for(Issue i : deadIssues) {
				i.display(curTime, timeInc);
				if(i.getCloseDate() > curTime+4*timeInc)
					deadIssues.remove(i);
			}
			
			parent.stroke(TEXTSTROKE);
			parent.fill(TEXTFILL);
			parent.textFont(fontBinName);
			parent.textAlign(PConstants.CENTER);
			parent.text(name, TEXTX, TEXTY);
			parent.text(issueCount, TEXTX, TEXTY+fontBinName.theight);
		}
		
		public void setProjectIssueId(int project_issue_id) {
			this.project_issue_id = project_issue_id;
		}

		public int getProjectIssueId() {
			return project_issue_id;
		}

		public void setName(String name) {
			this.name = name;
		}

		public String getName() {
			return name;
		}

		public void setDescription(String description) {
			this.description = description;
		}

		public String getDescription() {
			return description;
		}
	}
}